<?php
/**
 *
 */
namespace Yz\modules\Modules;

use Yz_Array,
    Yz_Hash;

class Model_CreateList
    extends \Yz\modules\Generic\Model_List
{

    protected $modulesList;


    /** re-check installed modules and update the database
     *
     *  @return chainable
     */
    public function updateModulesList()
    {
        $list = $this->listInstalledModules();
        $modelx = $this->module->selectModel("Default");
        foreach ($list as $item) {
            try {
                $modelx->addItem($item);
            } catch (\Yz_Exception $ex) {
                debug($ex);
            }
        }
        return $this;
    }


    /** Identify all modules installed in Yz/modules
     *
     *  @return Yz_Array[items]
     */
    protected function listInstalledModules()
    {
        $item0 = $this->selectItem();
        $modules = array();
        $path = \YZ_LIB . 'Yz/modules';
        $moduleDir = dir($path);
        while (false !== ($entry = $moduleDir->read())) {
            if ('.' !== substr($entry,0,1)) {
                include_once($path.'/'.$entry.'/'.'Module.php');
                $item = $this->createModuleItem($item0, $entry);
                if (false !== $item) {
                    $modules[$entry] = $item;
                }
            }
        }
        ksort($modules, SORT_STRING);
        return new Yz_Array($modules);
    }


    /**
     *  @param item $itemTpl item template
     *  @param string $moduleName
     *  @return item|false
     */
    protected function createModuleItem($itemTpl, $moduleName)
    {
        $class = "Yz\\modules\\{$moduleName}\\Module";
        $refl = new \ReflectionClass($class);
        if ($refl->isAbstract()) {
            return false;
        }
        $item = clone $itemTpl;
        $item->name = $moduleName;
        $item->isWebModule = (int)$refl->implementsInterface('Yz_Module_WebModule_Interface');
        return $item;
    }


    protected function createTables()
    {
        $tableList = array(
            'yz_modules',
            'yz_modules_actions',
        );
        $stmt = \Yz_SQL_Statement::create()
            ->query('DROP TABLE IF EXISTS ' . join(',', $tableList))
            ->execute()
        ;
        return $this
            ->createTableModules()
            ->createTableModulesActions()
        ;
    }


    protected function createTableModules()
    {
        $titleCols = $this->getTitleColumns();
        $q = <<<sql
CREATE TABLE yz_modules (
    id              smallint(5) unsigned NOT NULL PRIMARY KEY auto_increment
                    COMMENT 'restrict use only for DB relations',
    name            varchar(255) character set latin1 NOT NULL UNIQUE,
    isWebModule     tinyint(4) NOT NULL,
    {$titleCols}
    KEY(isWebModule)
)
sql;
        $stmt = \Yz_SQL_Statement::create()
            ->query($q)
            ->execute()
        ;
        return $this;
    }

    protected function createTableModulesActions()
    {
        $titleCols = $this->getTitleColumns();
        $q = <<<sql
CREATE TABLE yz_modules_actions (
    id              smallint(5) unsigned NOT NULL PRIMARY KEY auto_increment
                    COMMENT 'restrict use only for DB relations',
    name            varchar(255) character set latin1 NOT NULL,
    moduleId        smallint(5) unsigned NOT NULL,
    inMenu          tinyint(4) unsigned NOT NULL DEFAULT 0,
    accessType      tinyint(4) unsigned NOT NULL DEFAULT 0,
    className       varchar(255) character set latin1 NOT NULL DEFAULT '',
    arguments       varchar(255) character set latin1 NOT NULL DEFAULT '',
    {$titleCols}
    UNIQUE(moduleId, name)
)
sql;
        $stmt = \Yz_SQL_Statement::create()
            ->query($q)
            ->execute()
        ;
        return $this;
    }

    protected function getTitleColumns()
    {
        static $titleCols;
        if (null === $titleCols) {
            $titleCols = "";
            $langs = \I18N::getInstalledLanguages();
            foreach ($langs as $langCode => $langTitle) {
                $titleCols .= "    title_{$langCode} varchar(255) NOT NULL,\n";
            }
        }
        return $titleCols;
    }


    /** build the list of modules used by application, except abstract ones
     *  @return Yz_Hash {moduleName: ReflectionClass}
     */
    protected function buildModulesList()
    {
        $list =& $this->modulesList;
        if (null === $list) {
            $allNS = \Yz::getInstalledNamespaces();
            $list = array();
            foreach ($allNS as $ns) {
                $nlist = glob(\YZ_LIB . $ns . '/modules/*', GLOB_ONLYDIR);
                if ($nlist) {
                    foreach ($nlist as $modDir) {
                        $modName = basename($modDir);
                        if (! isset($list[$modName])) {
                            $file = $modDir . '/Module.php';
                            if (is_file($file)) {
                                include_once($file);
                            }
                            $class = $ns . '\\modules\\' . $modName . '\\Module';
                            $refl = new \ReflectionClass($class);
                            if ($refl->isAbstract()) {
                                continue;
                            }
                            if (! $refl->implementsInterface('Yz_Module_Interface')) {
                                \Log::log("Bad module class {$class}");
                                continue;
                            }
                            $list[$modName] = $refl;
                        }
                    }
                }
            }
            $list = new Yz_Hash($list);
        }
        return $list;
    }

    public function buildModulesIntlNames()
    {
        $list = $this->buildModulesList();
        $intlNames = array();
        $langs = array_keys(\I18N::getInstalledLanguages());
        foreach ($list as $modName => $refl) {
            $class = $refl->getName();
            $mod = $class::getInstance();
            try {
                $i18n = $mod->getI18N();
            } catch (\Yz_Exception $ex) {
                continue;
            }
            $intlNames[$modName] = array();
            foreach ($langs as $langCode) {
                $title = $i18n->translateToLang("#Module", $langCode);
                if ('#Module' === $title) {
                    $title = $modName;
                }
                $intlNames[$modName][$langCode] = $title;
            }
        }
        return $intlNames;
    }

    public function buildModulesActionLists()
    {
        $list = $this->buildModulesList();
        $actNames = array();
        foreach ($list as $modName => $refl) {
            $class = $refl->getName();
            $mod = $class::getInstance();
            try {
                $i18n = $mod->getI18N();
            } catch (\Yz_Exception $ex) {
                continue;
            }
            $ctlList = $this->buildControllersList($mod);
            $ctlList and
                $actNames[$modName] = $ctlList;
        }
        return $actNames;
    }



    /** get the list of all controllers defined in the module
     *  the method relies on the assumption that controller classes are not redefined in
     *  the namespaced constants
     *  @param Yz_Module_Interface $module
     *  @return array
     */
    public function buildControllersList(\Yz_Module_Interface $module)
    {
        debug();
        $list = array();
        $toScan = array($module) + class_parents($module);
        foreach ($toScan as $modClass) {
            debug("Scanning module {$modClass}");
            $ns = $modClass::namespaceName();
            if ("" === $ns) {
                break;
            }
            $path = strtr($ns, '\\', '/');
            $glob = YZ_LIB . '/' . $path . '/Controller/*.php';
            debug("Scanning path {$glob}");
            $files = glob($glob);
            if ($files) {
                foreach ($files as $file) {
                    $ctlName = basename($file, '.php');
                    debug("Scanning file {$file}, controller {$ctlName}");
                    if (! array_key_exists($ctlName, $list)) {
                        debug("Adding controller {$ctlName}");
                        include_once($file);
                        $class = $ns . '\\Controller_' . $ctlName;
                        $refl = new \ReflectionClass($class);
                        if (! $refl->isAbstract()) {
                            $ctlInfo = array(
                                'inMenu' => (int)$refl->getConstant('IN_MENU'),
                                'name' => $ctlName,
                                'className' => $class,
                                'accessType' => $refl->getConstant('ACCESS'),
                            );
                            $props = $refl->getDefaultProperties();
                            if (isset($props['arguments'])) {
                                $prop = $props['arguments'];
                                is_array($prop)
                                    and $prop
                                    and $ctlInfo['arguments'] = join(',', $prop);
                            }
                            $i18n = $module->getI18N();
                            foreach (array_keys($i18n->getInstalledLanguages()) as $lang) {
                                $ctlInfo['title_' . $lang] = $i18n->translateToLang(':' . $ctlName, $lang);
                            }
                            $list[$ctlName] = new Yz_Hash($ctlInfo);
                        }
                    }
                }
            }
        }
        return new Yz_Hash($list);
    }


}
